#include "app_flash.h"
#include "app_schd_task.h"

#define STM32_FLASH_BASE 0x08000000 
#define STM32_FLASH_PAGE_SIZE 1024//1k

static void flash_delay(uint32_t ms);
static uint8_t flash_debug = 0;

int flash_write(uint32_t address,uint8_t *buf,uint32_t length)
{
	if(!buf || !length) return -1;
	FLASH_Unlock();

	uint32_t end_address = address + length;
	uint16_t cnt = 0;
	uint32_t cpy_data_32 = 0;
	uint32_t offset = 1;
	uint16_t err_write_cnt = 0;
	
	FLASH_Status FlashStatus= FLASH_COMPLETE;
	FlashStatus = FLASH_WaitForLastOperation(5000);
	if(FlashStatus == FLASH_COMPLETE)
	{
		while (address<end_address)
		{
			cpy_data_32 = 0;
			if(end_address - address >= 4){
				//memcpy(cpy_data_32,buf,4);
				cpy_data_32 = buf[0+cnt]|(buf[1+cnt]<<8)|(buf[2+cnt]<<16)|(buf[3+cnt]<<24);
				//printf("w0 cpy_data_32:%d\r\n",cpy_data_32);
			}
			else{
				//memcpy(cpy_data_32,buf[cnt],length % 4);				
				for(uint16_t i=0;i<end_address - address;i++)
				{		
					cpy_data_32 += buf[i+cnt]*offset;
					offset = pow(2,(i+1)*8);		
				}
				//printf("w1 cpy_data_32:%d\r\n",cpy_data_32);
			}
			
			if(FLASH_ProgramWord(address, cpy_data_32) == FLASH_COMPLETE)
			{
				address = address + 4;
				cnt += 4;
			}
			else
			{
				printf("HAL_FLASH_Program is error.\r\n");			
				
				if(err_write_cnt++ > 100) 
				{
					FLASH_Lock();
					return -2;
				}
			}
		}
		FLASH_Lock();
		return 0;
	}
	else
	{
		FLASH_Lock();
		return -3;
	}
}


int flash_read(uint32_t address,uint8_t *buf,uint32_t length)
{

	if(!buf || !length) return -1;

	uint32_t end_address = address + length;
	__IO uint32_t data32 = 0;
	uint32_t cpy_data32  = 0;
	uint16_t cnt = 0;
	uint16_t offset = 0;
	while (address < end_address)
	{
		data32 = *(__IO uint32_t *)address;

		if(end_address - address >= 4){
			//printf("r0 cpy_data_32:%d\r\n",data32);
			buf[0+cnt] = data32&0xff;
			buf[1+cnt] = (data32>>8)&0xff;
			buf[2+cnt] = (data32>>16)&0xff;
			buf[3+cnt] = (data32>>24)&0xff;
		}
		else
		{
			//printf("r1 cpy_data_32:%d\r\n",data32);
			for(uint16_t i=0;i<end_address - address;i++)
			{
				buf[i+cnt] = (data32>>offset)&0xff;
				offset += 8;
			}
		}		
		address = address + 4;
		cnt += 4;
	}

	return 0;

}


int flash_erase(uint32_t address)
{
	FLASH_Unlock();

	if (FLASH_ErasePage(address) != FLASH_COMPLETE)
	{
		printf("HAL Flash Erase error!\r\n");

		FLASH_Lock();
		return -1;
	}

	FLASH_Lock();

	return 0;
}

static const uint32_t  flash_save_update_info_start_addr = STM32_FLASH_BASE + 110*1024;
int flash_write_raw(char *name,uint8_t *buf,uint32_t length)
{
	static uint16_t remain_len = 0;
	static uint32_t start_address = flash_save_update_info_start_addr;
	static uint32_t cur_address = 0;

	uint8_t read_buf[1024] = {0};
//	uint8_t *read_buf = malloc(1024);
//	if(!read_buf) 
//	{
//		printf("write no memery.\r\n");
//		return -1;
//	}
	
	if(!cur_address) //the first
	{
		cur_address = start_address;

		//flash_erase(start_address);
	}

	cur_address += length;

	if(cur_address - start_address > STM32_FLASH_PAGE_SIZE)
	{
		remain_len = (cur_address - start_address) % STM32_FLASH_PAGE_SIZE ;

		printf("remain len:%d\r\n",remain_len);

		uint16_t read_length = cur_address - start_address - length;
		//flash_read(start_address,read_buf,read_length);
		printf("read_length 1:%d\r\n",read_length);

		memcpy(&read_buf[read_length],buf,length - remain_len);
		printf("length - remain_len:%d\r\n",length - remain_len);
		flash_erase(start_address);
		flash_write(start_address,read_buf,read_length);

		flash_delay(1);
		start_address = cur_address - remain_len;

		
		flash_erase(start_address);
		flash_write(start_address,&buf[length - remain_len],remain_len);
		
	}
	else
	{
		uint16_t read_length = cur_address - start_address - length;
		if(read_length)
			flash_read(start_address,read_buf,read_length);
		printf("read_length 0:%d\r\n",read_length);

		memcpy(&read_buf[read_length],buf,length);
		flash_erase(start_address);
		flash_write(start_address,read_buf,read_length);
	}
	
	free(read_buf);
	
	return 1;
}

static void flash_delay(uint32_t ms)
{
	extern uint32_t GetTick(void);
	uint32_t cur_tick = 0;
	cur_tick = GetTick();

	while(cur_tick - GetTick() < ms);
}

void flash_test_loop(void)
{
	if(!flash_debug) return;

#if 1
	#define BIN_LEN 101
	char *name = "bin";
	uint8_t pkgs = 0;
	uint16_t len = BIN_LEN;
	
	uint8_t check_sum = 0;
	uint8_t write_buf[BIN_LEN] = {0};
	for(uint16_t i=0;i<BIN_LEN;i++)
	{
		write_buf[i] = i;
		
		check_sum += i;
	}
	printf("w-sum:%d\r\n",check_sum);

	pkgs = len / 100;
	if(len % 100) pkgs++;

	uint16_t cnt_len = 0;

	for(uint16_t i=0;i<pkgs;i++)
	{
		if(pkgs - i == 1)
			flash_write_raw(name,&write_buf[cnt_len],len % 100);
		else
			flash_write_raw(name,&write_buf[cnt_len],100);

		cnt_len += 100;
		flash_delay(50);
		//printf("cnt:%d,cur_len:%d,pkgs:%d\r\n",i,cnt_len,pkgs);
	}
	
	memset(write_buf,0,BIN_LEN);
	flash_read(flash_save_update_info_start_addr,write_buf,BIN_LEN);

	check_sum = 0;
	printf("\r\nr->");
	for(uint16_t cnt = 0;cnt<BIN_LEN;cnt++)
	{
		check_sum += write_buf[cnt];
	}
	printf("\r\nr-sum:%d\r\n",check_sum);
#endif

#if 0
	#define TEST_BUF_LEN 400
	
	uint32_t address = flash_save_update_info_start_addr;
	uint8_t check_sum = 0;
	uint8_t test_rxbuf[TEST_BUF_LEN] = {0};

	for(uint32_t page=1;page<=2;page++)
	{
		address += page*1024;
		
		flash_erase(address);

		check_sum = 0;
		uint8_t test_buf[TEST_BUF_LEN] = {0};
		printf("w->");
		for(uint16_t cnt = 0;cnt<TEST_BUF_LEN;cnt++)
		{
			test_buf[cnt] = cnt;
			printf("[%03d]",test_buf[cnt]);

			if(!(cnt%15))
				printf("\r\n");
			if(!(cnt%5))
				flash_delay(3);

			check_sum += cnt;
		}
		printf("\r\nw-sum:%d\r\n",check_sum);
		flash_write(address,test_buf,TEST_BUF_LEN);
		
		flash_delay(500);

		
		memset(test_rxbuf,0,TEST_BUF_LEN);
		flash_read(address,test_rxbuf,TEST_BUF_LEN);

		check_sum = 0;
		printf("\r\nr->");
		for(uint16_t cnt = 0;cnt<TEST_BUF_LEN;cnt++)
		{
			test_rxbuf[cnt] = cnt;
			printf("[%03d]",test_rxbuf[cnt]);
			
			if(!(cnt%15))
				printf("\r\n");
			if(!(cnt%5))
				flash_delay(3);

			check_sum += cnt;
		}
		printf("\r\nr-sum:%d\r\n",check_sum);
	}

	check_sum = 0;
	address = flash_save_update_info_start_addr;
	memset(test_rxbuf,0,TEST_BUF_LEN);
	flash_read(address,test_rxbuf,TEST_BUF_LEN);

	printf("\r\nr->");
	for(uint16_t cnt = 0;cnt<TEST_BUF_LEN;cnt++)
	{
		test_rxbuf[cnt] = cnt;
		printf("[%03d]",test_rxbuf[cnt]);
		
		if(!(cnt%15))
			printf("\r\n");
		if(!(cnt%5))
			flash_delay(3);

		check_sum += cnt;
	}
	printf("\r\nr-sum:%d\r\n",check_sum);
#endif

		flash_debug  = 0;
}

void flash_test(void)
{
	static uint8_t init_ok = 0;
	if(!init_ok)
	{
		init_ok = 1;
		schd_task_register(flash_test_loop,100);
	}
	
	flash_debug = 1;
}







